#include "hzpch.h"
#include "Log.h"
#include "Input.h"
#include "Application.h"

#include "SourceCode/Renderer/Renderer.h"
#include "SourceCode/Events/ApplicationEvent.h"

#include "SourceCode/Scene/Scene.h"

namespace Hazal 
{
	CScene scene;

	CApplication* CApplication::s_Instance = nullptr;

	CApplication::CApplication() : m_bRunning(true), m_bMinimized(false)
	{
		HZ_PROFILE_FUNCTION()

		HZ_ASSERT(!s_Instance, "Application already exists!")

		s_Instance = this;

		/// 创建GLFW窗口
		m_Window = Scope<CWindow>(CWindow::Create());

		/// 注册回调, 由App来响应m_Window的窗口事件
		m_Window->SetEventCallback(HZ_BIND_EVENT_FN(CApplication::OnEvent));

		/// 渲染初始化
		CRenderer::Init();

		/// 定义ImGuiLayer的unique_ptr
		m_ImGuiLayer = new CImGuiLayer();
		PushOverlay(m_ImGuiLayer);
	}

	CApplication::~CApplication()
	{
		CRenderer::Shutdown();
	}
	
	void CApplication::Run()
	{
		while (m_bRunning)
		{
			/// 自栈底向上, 对每一层进行更新, 窗口最小化时不更新.
			if (!m_bMinimized)
			{
				for (CLayer* layer : m_layerStack)
				{
					layer->OnUpdate(m_Window->GetFrameTime());
				}

				m_ImGuiLayer->Begin();
				for (CLayer* layer : m_layerStack)
				{
					layer->OnImGuiRender();
				}
				m_ImGuiLayer->End();
			}
			
			m_Window->OnUpdate();
		}
	}

	void CApplication::OnEvent(CEvent& e)
	{
		/// 处理具体的窗口关闭事件, 全局事件
		CEventDispatcher dispatcher(e);
		dispatcher.Dispatch<CWindowCloseEvent>(HZ_BIND_EVENT_FN(CApplication::OnWindowClose));
		dispatcher.Dispatch<CWindowResizeEvent>(HZ_BIND_EVENT_FN(CApplication::OnWindowResize));

		/// 自栈顶向下, 对每一层进行事件通知
		for (auto it = m_layerStack.rbegin(); it != m_layerStack.rend(); it++)
		{
			(*it)->OnEvent(e);
			
			/// 如果当前事件已经被处理, 直接退出遍历
			if (e.IsHandled())
			{
				break;
			}
		}
	}

	void CApplication::PushLayer(CLayer* layer)
	{
		m_layerStack.PushLayer(layer);
	}

	void CApplication::PushOverlay(CLayer* overlay)
	{
		m_layerStack.PushOverlay(overlay);
	}

	bool CApplication::OnWindowClose(CWindowCloseEvent& e)
	{
		m_bRunning = false;
		return true;
	}

	bool CApplication::OnWindowResize(CWindowResizeEvent& e)
	{
		if (0 == e.GetWidth() || 0 == e.GetHeight())
		{
			m_bMinimized = true;
			return false;
		}

		CRenderer::OnWindowResize(e.GetWidth(), e.GetHeight());
		m_bMinimized = false;
		
		return false;
	}
}
